home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Applications / Nuntius 1.2 / src / Nuntius / UDiscList.cp < prev    next >
Encoding:
Text File  |  1994-02-20  |  17.9 KB  |  707 lines  |  [TEXT/MPS ]

  1. // Copyright © 1992 Peter Speck, speck@dat.ruc.dk. All rights reserved.
  2. // UDiscList.cp
  3.  
  4. #include "UDiscList.h"
  5. #include "UDiscussion.h"
  6. #include "UHashTable.h"
  7. #include "Tools.h"
  8. #include "StreamTools.h"
  9. #include "UThread.h"
  10.  
  11. #include <ErrorGlobals.h>
  12.  
  13. #include <Packages.h>
  14.  
  15. #pragma segment MyGroup
  16.  
  17. #define qDebugDBUpdate qDebug & 0
  18. #define qDebugDeleteOldStuff qDebug & 0
  19.  
  20. const Boolean gHeapCheckDiscList = false;
  21.  
  22. const short kNone = -1;
  23.  
  24. const long kCurrentDLVersion = 1;
  25. const long kMinDLVersion = 1;
  26.  
  27. void DebugMsg(const char *p)
  28. {
  29. #if qDebug
  30.     ProgramBreak(p);
  31. #endif
  32.     Failure(errDatabaseScrambled, 0);
  33.     p = p;
  34. }
  35.  
  36. PDiscList::PDiscList()
  37.     : PStatDynArray()
  38. {
  39.     fHashTable = nil;
  40. }
  41.  
  42. void PDiscList::IDiscList()
  43. {
  44.     if (gHeapCheckDiscList)
  45.         DebugStr("Entering PDiscList::IDiscList();hc;g");
  46.     IStatDynArray(4 * 1024, 30);
  47.     FailInfo fi;
  48.     if (fi.Try())
  49.     {
  50.         THashTable *ht = new THashTable();
  51.         ht->IHashTable();
  52.         fHashTable = ht;
  53.         fHashTable->FillHashTable(kNone);
  54.         fKillDiscussionTime = 3 * 24 * 60 * 60;
  55. #if qDebugDBUpdate & qDebug
  56.         fprintf(stderr, "*** Testing update of the database of discussions\n");
  57. #endif
  58.         fi.Success();
  59.     }
  60.     else // fail
  61.     {
  62.         delete this;
  63.         fi.ReSignal();
  64.     }
  65.     if (gHeapCheckDiscList)
  66.         DebugStr("Leaving PDiscList::IDiscList();hc;g");
  67. }
  68.  
  69. PDiscList::~PDiscList()
  70. {
  71.     if (gHeapCheckDiscList)
  72.         DebugStr("Entering PDiscList::Free();hc;g");
  73.     FreeIfObject(fHashTable); fHashTable = nil;
  74.     if (gHeapCheckDiscList)
  75.         DebugStr("Leaving PDiscList::Free();hc;g");
  76. }
  77.  
  78. void PDiscList::DoRead(TStream *aStream)
  79. {
  80.     if (gHeapCheckDiscList)
  81.         DebugStr("Entering PDiscList::DoRead();hc;g");
  82.     long version = aStream->ReadLong();
  83.     MyStreamCheckVersion(version, kMinDLVersion, kCurrentDLVersion, "PDiscList");
  84.     PStatDynArray::DoRead(aStream);
  85.     fHashTable->DoRead(aStream);
  86. #if qDebug & 0
  87.     if (!PDiscList::SanityCheck())
  88.         Failure(errDatabaseScrambled, 0);
  89. #endif
  90. #if qDebugCDiscussion & 0
  91.     fprintf(stderr, "DiscList dump after read from disc:\n");
  92.     DebugDump(true);
  93. #endif
  94.     if (gHeapCheckDiscList)
  95.         DebugStr("Leaving PDiscList::DoRead();hc;g");
  96. }
  97.  
  98. void PDiscList::DoWrite(TStream *aStream)
  99. {
  100.     if (gHeapCheckDiscList)
  101.         DebugStr("Entering PDiscList::DoWrite();hc;g");
  102.     aStream->WriteLong(kCurrentDLVersion);
  103.     PStatDynArray::DoWrite(aStream);
  104.     fHashTable->DoWrite(aStream);
  105.     if (gHeapCheckDiscList)
  106.         DebugStr("Leaving PDiscList::DoWrite();hc;g");
  107. }
  108.  
  109. long PDiscList::NeededDiskSpace()
  110. {
  111.     if (gHeapCheckDiscList)
  112.         DebugStr("Entering PDiscList::DoNeedDiskSpace();hc;g");
  113.     long needed = 
  114.                         sizeof(long) + // version
  115.                         PStatDynArray::NeededDiskSpace();
  116.     fHashTable->DoNeedDiskSpace(needed);
  117.     if (gHeapCheckDiscList)
  118.         DebugStr("Leaving PDiscList::DoNeedDiskSpace();hc;g");
  119.     return needed;
  120. }
  121.  
  122. void PDiscList::DeleteAll()
  123. {
  124.     if (gHeapCheckDiscList)
  125.         DebugStr("Entering PDiscList::DeleteAll();hc;g");
  126.     fHashTable->FillHashTable(kNone);
  127.     PStatDynArray::DeleteAll();
  128.     if (gHeapCheckDiscList)
  129.         DebugStr("Leaving PDiscList::DeleteAll();hc;g");
  130. }
  131.  
  132. ArrayIndex PDiscList::CreateDiscussion(long id, 
  133.                                         HandleOffsetLength msgIDHol, 
  134.                                         HandleOffsetLength nameHol)
  135. {
  136.     if (gHeapCheckDiscList)
  137.         DebugStr("Entering PDiscList::CreateDiscussion();hc;g");
  138.     long oldSize = GetSize();
  139.     VOLATILE(oldSize);
  140.     FailInfo fi;
  141.     if (fi.Try())
  142.     {
  143.         const short extraSize = 24 + 16; // articleID's + extra space
  144.         short entrySize = sizeof(CDiscussion) + extraSize + short(msgIDHol.fLength + nameHol.fLength);
  145.         entrySize = (entrySize + 3) & ~3; // round it
  146.         ArrayIndex index = CreateNewElement(entrySize);
  147. #if qDebugCDiscussion
  148.         fprintf(stderr, "Created new entry for CDiscussion: %ld bytes\n", entrySize);
  149.         Boolean prevLock = LockArray(true);
  150. #endif
  151.         CDiscussion *discP = (CDiscussion*) ComputeAddress(index);
  152.         discP->Initialize(short(GetElementSize(index)) - sizeof(CDiscussion), kNone);
  153.         discP->SetLink(kNone);
  154. #if qDebugCDiscussion
  155.         LockArray(prevLock);
  156. #endif
  157.         AddArticleAsOriginator(index, id, msgIDHol, nameHol);
  158. #if qDebugCDiscussion
  159.         SanityCheck();
  160. #endif
  161.         fi.Success();
  162.         return GetSize();
  163.     }
  164.     else // fail
  165.     {
  166.         // fSize = oldSize;
  167.         while (GetSize() > oldSize)
  168.             DeleteElementAt(GetSize());
  169.         fi.ReSignal();
  170.     }
  171.     if (gHeapCheckDiscList)
  172.         DebugStr("Leaving PDiscList::CreateDiscussion();hc;g");
  173. }
  174.  
  175. //-----------------
  176.  
  177. void PDiscList::ExpandDiscussion(ArrayIndex discIndex, short spaceNeeded)
  178. {
  179.     if (gHeapCheckDiscList)
  180.         DebugStr("Entering PDiscList::ExpandDiscussion();hc;g");
  181. #if qDebug
  182. //    SanityCheck();
  183.     if (spaceNeeded < 0)
  184.     {
  185.         fprintf(stderr, "***** PDiscList::ExpandDiscussion(), index = %ld, spaceNeeded = %ld\n", discIndex, spaceNeeded);
  186.         ProgramBreak(gEmptyString);
  187.         return;
  188.     }
  189. #endif
  190.     short newSize = short(GetElementSize(discIndex)) + spaceNeeded;
  191. #if qDebugCDiscussion
  192.     fprintf(stderr, "Expanding CDiscussion/PDynDynArray entry %ld -> %ld (Δ = %ld)\n", GetElementSize(discIndex), long(newSize), long(spaceNeeded));
  193. #endif
  194.     SetElementSize(discIndex, newSize);
  195. #if qDebug
  196. //    SanityCheck();
  197. #endif
  198.     ComputeAddress(discIndex)->UpdateAlloc(newSize - sizeof(CDiscussion));
  199. #if qDebug
  200. //    SanityCheck();
  201. #endif
  202.     if (gHeapCheckDiscList)
  203.         DebugStr("Leaving PDiscList::ExpandDiscussion();hc;g");
  204. }
  205.  
  206. void PDiscList::AddArticle(ArrayIndex discIndex, long id)
  207. {
  208.     if (gHeapCheckDiscList)
  209.         DebugStr("Entering PDiscList::AddArticle();hc;g");
  210. #if qDebugCDiscussion
  211.     SanityCheck();
  212. #endif
  213.     short spaceNeeded;
  214.     do
  215.     {
  216. #if qDebugCDiscussion
  217.         Boolean prevLock = LockArray(true);
  218. #endif
  219.         CDiscussion *discP = ComputeAddress(discIndex);
  220.         spaceNeeded = discP->AddArticle(id);
  221. #if qDebugCDiscussion
  222.         LockArray(prevLock);
  223. #endif
  224.         if (spaceNeeded) 
  225.             ExpandDiscussion(discIndex, spaceNeeded);
  226.     } 
  227.     while (spaceNeeded);
  228. #if qDebugCDiscussion
  229.     SanityCheck();
  230. #endif
  231.     if (gHeapCheckDiscList)
  232.         DebugStr("Leaving PDiscList::AddArticle();hc;g");
  233. }
  234.  
  235. void PDiscList::AddArticleAsOriginator(ArrayIndex discIndex, long id, 
  236.                                 HandleOffsetLength msgIDHol, 
  237.                                 HandleOffsetLength nameHol)
  238. {
  239.     if (gHeapCheckDiscList)
  240.         DebugStr("Entering PDiscList::AddArticleAsOriginator();hc;g");
  241.     UnchainDiscussion(discIndex);
  242.     short hash = fHashTable->HashMessageID(*msgIDHol.fH + msgIDHol.fOffset, msgIDHol.fLength);
  243.     ArrayIndex link = fHashTable->GetValue(hash);
  244.     short spaceNeeded;
  245.     do
  246.     {
  247. #if qDebugCDiscussion
  248.         Boolean prevLock = LockArray(true);
  249. #endif
  250.         CDiscussion *discP = ComputeAddress(discIndex);
  251.         spaceNeeded = discP->AddArticleAsOriginator(id, msgIDHol, hash, link, nameHol);
  252. #if qDebugCDiscussion
  253.         LockArray(prevLock);
  254. #endif
  255.         if (spaceNeeded) 
  256.             ExpandDiscussion(discIndex, spaceNeeded);
  257.     } 
  258.     while (spaceNeeded);    
  259.     fHashTable->SetValue(hash, discIndex);
  260. #if qDebugCDiscussion
  261.     SanityCheck();
  262. #endif
  263.     if (gHeapCheckDiscList)
  264.         DebugStr("Leaving PDiscList::AddArticleAsOriginator();hc;g");
  265. }
  266.  
  267. void PDiscList::GetName(ArrayIndex discIndex, CStr255 &name)
  268. {
  269. #if qDebug
  270.     if (discIndex < 1 || discIndex > GetSize())
  271.         ProgramBreak("invalid index");
  272. #endif
  273.     ComputeAddress(discIndex)->GetName(name);
  274. }
  275.  
  276. unsigned long PDiscList::GetLastActiveDateTime(ArrayIndex discIndex)
  277. {
  278.     return ComputeAddress(discIndex)->GetLastActiveDateTime();
  279. }
  280.  
  281. //----------------- executing with CDiscussion locked!
  282. TLongintList *PDiscList::GetArticleIDList(ArrayIndex discIndex)
  283. {
  284.     Boolean prevLock = LockArray(true);
  285.     CDiscussion *discP = ComputeAddress(discIndex);
  286.     TLongintList *list = discP->GetArticleIDList();
  287.     LockArray(prevLock);
  288.     return list;
  289. }
  290.  
  291. //----------------- 
  292. long PDiscList::GetArticleID(ArrayIndex discIndex, long articleIndex)
  293. {
  294.     return ComputeAddress(discIndex)->GetArticleID(articleIndex);
  295. }
  296.  
  297. short PDiscList::GetNoArticles(ArrayIndex discIndex)
  298. {
  299.     return ComputeAddress(discIndex)->GetNoArticles();
  300. }
  301.  
  302. //-----------------
  303. CDiscussion *PDiscList::FindDiscussionFromRef(char*& refP, long refLen, 
  304.                                                                                             short hash, ArrayIndex &discIndex)
  305. {
  306.     discIndex = fHashTable->GetValue(hash);
  307.     if (discIndex == kNone)
  308.         return nil;
  309.     CDiscussion *discP = ComputeAddress(discIndex);
  310.     while (true) 
  311.     {
  312.         if (discP->CompareMsgID(refP, refLen))
  313.             return discP;
  314.         discIndex = discP->GetLink();
  315.         if (discIndex == kNone)
  316.             return nil;
  317.         discP = ComputeAddress(discIndex);
  318.     }
  319. }
  320.  
  321. Boolean PDiscList::FindNextReference(char *&refP, long &refLen, short &hash)
  322. // no mem-move allowed
  323. {
  324.     while (*refP != '<')  // quick and dirty parsing
  325.     { 
  326.         if (*refP == 13)
  327.             return false;
  328.         refP++;
  329.     }
  330.     hash = fHashTable->HashMessageID(refP, refLen);
  331.     return true;
  332. }
  333.  
  334. Boolean PDiscList::TryFindDiscussion(HandleOffsetLength idHol,
  335.                                                                         ArrayIndex &discIndex)
  336. {
  337.     char *refP = *idHol.fH + idHol.fOffset;
  338.     long refLen;
  339.     while (true) 
  340.     {
  341.         short hash;
  342.         if(!FindNextReference(refP, refLen, hash))
  343.             return false;
  344.         if (FindDiscussionFromRef(refP, refLen, hash, discIndex))
  345.             return true;
  346.         refP += refLen;
  347.     }
  348. }
  349.  
  350. //-------------------
  351. void PDiscList::UnchainDiscussion(ArrayIndex index)
  352. {
  353.     if (gHeapCheckDiscList)
  354.         DebugStr("Entering PDiscList::UnchainDiscussion();hc;g");
  355.     CDiscussion *theDiscP = ComputeAddress(index);
  356.     short hash = theDiscP->GetHash();
  357.     if (hash == kNone)
  358.     {
  359.         if (gHeapCheckDiscList)
  360.             DebugStr("Leaving PDiscList::UnchainDiscussion();hc;g");
  361.         return;
  362.     }
  363.     ArrayIndex hashValue = fHashTable->GetValue(hash);
  364.     if (hashValue == kNone)
  365.     {
  366.         DebugMsg("HashTable/DiscLink damaged (Hashtable value is kNone)");
  367.         SanityCheck();
  368.         Failure(errDatabaseScrambled, 0); // should be "nice" dialog to the user
  369.         return;
  370.     }
  371.     if (hashValue == index) 
  372.     {
  373.         fHashTable->SetValue(hash, theDiscP->GetLink());
  374.         theDiscP->SetLink(kNone);
  375.         if (gHeapCheckDiscList)
  376.             DebugStr("Leaving PDiscList::UnchainDiscussion();hc;g");
  377.         return;
  378.     }
  379.     CDiscussion *aDiscP = ComputeAddress(hashValue);
  380.     while (true)
  381.     {
  382.         if (aDiscP->GetLink() == kNone) 
  383.         {
  384.             DebugMsg("DiscLink damaged (not found in linked list)"); // should be "nice" dialog to the user //?
  385.             SanityCheck();
  386.             Failure(errDatabaseScrambled, 0);
  387.             return;
  388.         }
  389.         if (aDiscP->GetLink() == index) 
  390.         {
  391.             aDiscP->SetLink(theDiscP->GetLink());
  392.             theDiscP->SetLink(kNone);
  393.             if (gHeapCheckDiscList)
  394.                 DebugStr("Leaving PDiscList::UnchainDiscussion();hc;g");
  395.             return;
  396.         }
  397.         aDiscP = ComputeAddress(aDiscP->GetLink());
  398.     }
  399. #if qDebugCDiscussion
  400.     SanityCheck();
  401. #endif
  402.     if (gHeapCheckDiscList)
  403.         DebugStr("Leaving PDiscList::UnchainDiscussion();hc;g");
  404. }
  405.  
  406. TLongintList *PDiscList::FindDiscussionsToDelete(long firstAvailableArticleID, long /* lastAvailableArticleID */)
  407. {
  408.     if (gHeapCheckDiscList)
  409.         DebugStr("Entering PDiscList::FindDiscussionsToDelete();hc;g");
  410.     FailInfo fi;
  411.     TLongintList *itemsToDelete = nil; // no need for TSortedLongintlist, looping in increasing order
  412.     VOLATILE(itemsToDelete);
  413.     if (fi.Try()) 
  414.     {
  415.         unsigned long dateTime;
  416.         GetDateTime(dateTime);
  417.         TLongintList *list = new TLongintList();
  418.         list->ILongintList();
  419.         itemsToDelete = list;
  420.         itemsToDelete->fAllocationIncrement = 50;
  421.         ArrayIndex size = GetSize();
  422.         for (ArrayIndex index = 1; index <= size; index++) 
  423.         {
  424.             if (EntryIsFree(index))
  425.                 continue;
  426.             CDiscussion *discP = ComputeAddress(index);
  427. #if 1
  428.             Boolean deleteIt =  (discP->GetLastArticleID() < firstAvailableArticleID);
  429. #else
  430.             Boolean deleteIt = ((discP->GetArticleID(1) < firstAvailableArticleID) &&
  431.                                                     (dateTime - discP->GetLastActiveDateTime() > fKillDiscussionTime)
  432.                                                  ) || (discP->GetLastArticleID() < firstAvailableArticleID);
  433. #endif
  434. #if qDebugDBUpdate
  435.             if (Random() > 25000)
  436.                 deleteIt = true;
  437. #endif
  438.             if (deleteIt)
  439.                 itemsToDelete->InsertLast(index);
  440.             if ((index & 15) == 0)
  441.                 gCurThread->CheckYield();
  442.         } // iter
  443.         fi.Success();
  444.         return itemsToDelete;
  445.     }
  446.     else // fail
  447.     {
  448.         FreeIfObject(itemsToDelete); itemsToDelete = nil;
  449.         fi.ReSignal();
  450.     }
  451.     if (gHeapCheckDiscList)
  452.         DebugStr("Leaving PDiscList::FindDiscussionsToDelete();hc;g");
  453. }
  454.  
  455. void PDiscList::DeleteOldStuff(long firstAvailableArticleID, long lastAvailableArticleID)
  456. {
  457.     if (gHeapCheckDiscList)
  458.         DebugStr("Entering PDiscList::DeleteOldStuff();hc;g");
  459. #if qDebugDeleteOldStuff
  460.     fprintf(stderr, "\n");
  461.     fprintf(stderr, ">>> DeleteOldStuff()\n");
  462.     SanityCheck();
  463. #endif
  464.     TLongintList *itemsToDelete;
  465.     VOLATILE(itemsToDelete);
  466.     FailInfo fi;
  467.     if (fi.Try()) 
  468.     {
  469.         itemsToDelete = FindDiscussionsToDelete(firstAvailableArticleID, lastAvailableArticleID);
  470.         if (itemsToDelete->GetSize() == 0) 
  471.         {
  472. #if qDebugDeleteOldStuff
  473.             fprintf(stderr, "No discussions to be deleted for now.\n");
  474. #endif
  475.             itemsToDelete->Free();
  476.             fi.Success();
  477.             return;
  478.         }
  479.         {
  480.             CLongintIterator iter(itemsToDelete);
  481.             long index;
  482. #if qDebugDeleteOldStuff        
  483.             fprintf(stderr, "Discussions to delete (%ld entries):\n", itemsToDelete->GetSize());
  484.             long zzz = 0;
  485.             for (index = iter.FirstLong(); iter.More(); index = iter.NextLong())
  486.             {
  487.                 fprintf(stderr, "%ld, ", index);
  488.                 if ((zzz++ % 10) == 0)
  489.                     fprintf(stderr, "\n");
  490.             }
  491.             fprintf(stderr, "\nStarting to delete them...\n");
  492.             zzz = 0;
  493. #endif
  494.             long nr = 0;
  495.             for (index = iter.FirstLong(); iter.More(); index = iter.NextLong())
  496.             {
  497. #if qDebugDeleteOldStuff
  498.                 fprintf(stderr, "%ld, ", index);
  499.                 if ((zzz++ % 10) == 0)
  500.                     fprintf(stderr, "\n");
  501. #endif
  502.                 UnchainDiscussion(index);
  503.                 DeleteElementAt(index);
  504.                 if ((nr++ & 15) == 0)
  505.                     gCurThread->CheckYield();
  506.             }
  507.             itemsToDelete->Free(); itemsToDelete = nil;
  508.         }
  509.  
  510. #if qDebugDeleteOldStuff
  511.         fprintf(stderr, "\n");
  512.         SanityCheck();
  513.         fprintf(stderr, "<<< DeleteOldStuff()\n");
  514.         fprintf(stderr, "\n");
  515. #endif
  516.         fi.Success();
  517.     }
  518.     else // fail
  519.     {
  520.         FreeIfObject(itemsToDelete); itemsToDelete = nil;
  521.         fi.ReSignal();
  522.     }
  523.     if (gHeapCheckDiscList)
  524.         DebugStr("Leaving PDiscList::DeleteOldStuff();hc;g");
  525. }    
  526.  
  527.  
  528. // ---------------------------
  529. void PDiscList::DebugDump(Boolean verbose)
  530. {
  531.     if (gHeapCheckDiscList)
  532.         DebugStr("Entering PDiscList::DebugDump();hc;g");
  533.     PStatDynArray::DebugDump(verbose || true);
  534. #if qDebug
  535.     if (1)
  536.     {
  537.         Boolean prevLock = LockArray(true);
  538.         CStr255 s, num;
  539.         unsigned long uli;
  540.         GetDateTime(uli);
  541.         NumToString(long(uli) & 0xFFFFF, num);
  542.         s = "HD2:Desktop folder:disc_";
  543.         s += num;
  544.         fprintf(stderr, "Dumping discussions to %s\n", (char*)s);
  545.         FILE *file = fopen(s, "w");
  546.         if (!file)
  547.             ProgramBreak("Could not creat file to dump discussions into");
  548.         else
  549.         {
  550.             fsetfileinfo(s, 'MPS ', 'TEXT');
  551.             for (ArrayIndex i = 1; i <= GetSize(); i++)
  552.             {
  553.                 fprintf(file, "%ld: ", i);
  554.                 if (EntryIsFree(i))
  555.                     fprintf(file, "is free entry (size: %ld bytes)\n\n", GetElementSize(i));
  556.                 else
  557.                     ComputeAddress(i)->Dump(file, i);
  558.             }
  559.             fHashTable->DebugDump(file);
  560.             fclose(file);
  561.         }
  562.         LockArray(prevLock);
  563.     }
  564.     long freeSpace = 0;
  565.     for (ArrayIndex i = 1; i <= GetSize(); i++)
  566.     {
  567.         if (!EntryIsFree(i))
  568.             freeSpace += ComputeAddress(i)->GetFreeSpace();
  569.     }
  570.     fprintf(stderr, "Free space in CDiscussions: %ld bytes\n", freeSpace);    
  571.     DumpHashTableUsage();
  572. #endif
  573.     if (gHeapCheckDiscList)
  574.         DebugStr("Leaving PDiscList::DebugDump();hc;g");
  575. }
  576.  
  577.  
  578. void PDiscList::DumpHashTableUsage()
  579. {
  580.     fprintf(stderr, "Hash table entry count:\n");
  581.     for (ArrayIndex index = 0; index < kHashTableEntries; index++) 
  582.     {
  583.         if (index % 20 == 0)
  584.             fprintf(stderr, "%4ld: ", index);
  585.         long discIndex = fHashTable->GetValue(short(index));
  586.         long num = 0;
  587.         while (discIndex != kNone)
  588.         {
  589.             num++;
  590.             CDiscussion *discP = ComputeAddress(discIndex);
  591.             discIndex = discP->GetLink();
  592.         }
  593.         if (!num)
  594.             fprintf(stderr, "  -");
  595.         else
  596.             fprintf(stderr, "%3ld", num);
  597.         if ((index + 1) % 20 == 0)
  598.             fprintf(stderr, "\n");
  599.     }
  600.     fprintf(stderr, "\n");
  601. }
  602.  
  603. Boolean PDiscList::SanityCheck()
  604. {
  605.     Boolean isGood = PStatDynArray::SanityCheck();
  606. #if qDebug
  607.     if (isGood)
  608.     {
  609.         for (long index = 1; index <= GetSize(); index++)
  610.         {
  611.             if (!ComputeAddress(index)->SanityCheck(index))
  612.                 isGood = false;
  613.         }
  614.         if (!SanityCheckLinks())
  615.             isGood = false;
  616.         if (!isGood)
  617.         {
  618.             ProgramBreak("DiscList is BAD!");
  619.             DebugDump(true);
  620.         }
  621.     }
  622. #endif
  623.     return isGood;
  624. }
  625.  
  626. Boolean PDiscList::SanityCheckLinks()
  627. {
  628.     Boolean isGood = true;
  629. #if qDebug
  630.     Boolean prevLock = LockDataHandle(true, false);
  631.     // this code should check for:
  632.     //   a) all discussions are linked in the proper links
  633.     //   b) all discussions are in the linked lists
  634.     // part a:
  635.     TSortedLongintList *foundDiscs = new TLongintList();
  636.     foundDiscs->ISortedLongintList();
  637.     for (ArrayIndex hash = 0; hash < kHashTableEntries; hash++) 
  638.     {
  639.         long discIndex = fHashTable->GetValue(short(hash));
  640.         if (discIndex == kNone)
  641.             continue;
  642.         if (discIndex < 1 || discIndex > GetSize() || EntryIsFree(discIndex))
  643.         {
  644.             fprintf(stderr, "WRONG:: PDiscList::SanityCheckLinks, got bad value from hashtable: %ld, hash = %ld\n", discIndex, hash);
  645.             isGood = false;
  646.             continue;
  647.         }
  648.         while (true)
  649.         {
  650.             foundDiscs->Insert(discIndex);
  651.             CDiscussion *discP = ComputeAddress(discIndex);
  652.             long discHash = discP->GetHash();
  653.             long nextDisc = discP->GetLink();
  654.             if (discHash != hash)
  655.             {
  656.                 fprintf(stderr, "WRONG:: PDiscList::SanityCheckLinks, discussion is in wrong list.\n");
  657.                 fprintf(stderr, "-       linkHash = %ld, discHash = %ld, discIndex = %ld\n", hash, discHash, discIndex);
  658.                 isGood = false;
  659.             }
  660.             if (nextDisc == kNone)
  661.                 break;
  662.             if (nextDisc < 1 || nextDisc > GetSize())
  663.             {
  664.                 fprintf(stderr, "WRONG:: PDiscList::SanityCheckLinks, got bad link from disc: %ld, index = %ld, GetSize() = %ld\n", nextDisc, discIndex, GetSize());
  665.                 isGood = false;
  666.                 break;
  667.             }
  668.             if (EntryIsFree(nextDisc))
  669.             {
  670.                 fprintf(stderr, "WRONG:: PDiscList::SanityCheckLinks, got bad link from disc: %ld (which is a free entry!), discIndex = %ld\n", nextDisc, discIndex);
  671.                 isGood = false;
  672.                 // here we try to check the free empty, otherwise put a break here
  673.             }
  674.             discIndex = nextDisc;
  675.         } // while
  676.     } // hash table loop
  677.     // part b:
  678.     for (ArrayIndex index = 1; index < GetSize(); index++) 
  679.     {
  680.         if (foundDiscs->GetIdentityItemNo(index) != kEmptyIndex)
  681.         {
  682.             if (EntryIsFree(index))
  683.             {
  684.                 fprintf(stderr, "WRONG:: PDiscList::SanityCheckLinks, an empty entry (index = %ld) is linked into the list\n", index);
  685.                 isGood = false;
  686.             }
  687.         }
  688.         else
  689.         {
  690.             if (!EntryIsFree(index))
  691.             {
  692.                 fprintf(stderr, "WRONG:: PDiscList::SanityCheckLinks, a discussion is not included in the linked lists\n");
  693.                 fprintf(stderr, "-       discIndex = %ld, here it comes:\n", index);
  694.                 isGood = false;
  695.                 ComputeAddress(index)->Dump(stderr, index);
  696.             }
  697.         }
  698.     }
  699.     FreeIfObject(foundDiscs); foundDiscs = nil;
  700.     LockDataHandle(prevLock, false);
  701.     if (!isGood)
  702.         DebugDump(true);
  703. #endif
  704.     return isGood;
  705. }
  706.  
  707.